home *** CD-ROM | disk | FTP | other *** search
/ Inside Mac Games Volume 4 #4 / IMG 36 April 1996.iso / Essentials / MGD 4⁄96ƒ / MGW1Codeƒ / MGWSound1.c < prev    next >
Text File  |  1996-04-01  |  26KB  |  597 lines

  1. //==============================================================================================\\
  2. //        -------------------------------------------------------------------------------            \\
  3. //        MGWSound1.c version 1.0.0    copyright © 1993…1995 Jamie McCornack, john calhoun            \\
  4. //        -------------------------------------------------------------------------------            \\
  5. //            *<-- FOR 68K CODE ONLY -->*
  6. //         Sound management utilities for Macintosh GameWriter 1.0.0, a training program…            \\
  7. //        …for beginning Mac game programmers. MGW1 includes MGWExterns1.h, MGWUtilities1.c,…        \\
  8. //        …MGWSound1.c, MGWGraphics1.c, MGWGraphicsBWLite1.c, HelloWorld.rsrc and an assortment…    \\
  9. //        …of demo programs; projects HelloWorld1.π etc. and source code files HelloWorld1.c etc.    \\
  10. //         A tutorial is available in Tricks of the Mac Game Programming Gurus, published…        \\
  11. //        …by Hayden Books, August 1995.                                                                        \\
  12. //                                                                                                \\
  13. //        This code is offered by the copyright holders for no fee and for whatever use…            \\
  14. //        …you care to make of it, but we do hope you remember where it came from.                \\
  15. //                                                                                                \\
  16. //        Please send bug reports to MacGameDev at America OnLine.    macgamedev@aol.com            \\
  17. //        Suggestions and observations are also appreciated.                                        \\
  18. //        Updates and upgrades will be available now and then from the above e-mail address.        \\
  19. //==============================================================================================\\
  20.  
  21. #include "MGWExterns1.h"
  22. #include <Sound.h>
  23.  
  24. // Here is the constant that the SoundUnit uses internally
  25. #define        kOurCallBack    911    // This arbitrary number will identify a call back as our own.
  26.  
  27. // Here is the "global" variable used by the sound routines.
  28. Boolean        gUserWantsSound;    // A user-set variable used for turning on/off all sounds.
  29.  
  30. // Here are the internal variables used by MGWSound1.c. If you use the proper calls…
  31. // …(see the routines in MGWExterns.h) you should never need to access these externally.
  32. SndChannelPtr    soundChannel;    // This is the sound channel all our routines use
  33. short            soundPriority;    // This is the priority of the sound currently playing
  34. Boolean            canUseSound;    // Is FALSE if we can't use sound on this Mac
  35.  
  36.  
  37. //------------------------------------------------------------ Prototypes
  38.  
  39. void    FlushSoundNow (void);
  40. void    FlushSoundSoon (void);
  41. pascal void    SoundCallBack(SndChannelPtr chan, SndCommand theCommand);
  42. OSErr    InstallCallBack(void);
  43.  
  44.  
  45. //==============================================================  Functions
  46.  
  47. //--------------------------------------------------------------  CloseDownSound
  48.  
  49. // Call this function to dispose of the sound channel when switching out or quitting.
  50. // Otherwise, sound may be disabled for the next program you run.
  51.  
  52. OSErr CloseDownSound(void)
  53. {
  54.     OSErr    theErr;
  55.     
  56.     theErr = noErr;                        // Assume no error
  57.     
  58.     if (soundChannel != nil) {
  59.         theErr = SndDisposeChannel(soundChannel, TRUE);
  60.         soundChannel = nil;                // Set the variable to nil now
  61.     }
  62.     
  63.     soundPriority = kNoSoundPlaying;    // soundPriority needs to reflect no sound playing
  64.     
  65.     return(theErr);
  66. }
  67.  
  68. //--------------------------------------------------------------  InitializeForSound
  69.  
  70. // This procedure initializes all variables used by the sound unit routines.
  71. // It also does a check using SysEnvirons() to determine if sound will run at all.
  72. // You should have your program call this function only once at start up!
  73.  
  74. void    InitializeForSound()
  75. {
  76.     SysEnvRec    thisWorld;
  77.     
  78.     gUserWantsSound = TRUE;
  79.  
  80.     soundChannel = nil;                    // Initialize our sound channel to nil (very important).
  81.     soundPriority = kNoSoundPlaying;    // Indicate no sound is playing.
  82.  
  83.     SysEnvirons(1, &thisWorld);            // Call on SysEnvirons() to determine if we can play
  84.                                         // sound on this particular Mac and System version.
  85.                                         
  86.                     // Anything before the Mac II may not work for these routines.
  87.                     // Anything before System 6.0.5 may not work for these routines.
  88.     canUseSound = ((thisWorld.machineType >= envMacII) && (thisWorld.systemVersion >= 0x0605));
  89. }
  90.  
  91. //--------------------------------------------------------------  LoadASound
  92.  
  93. // Call this function to load a specific sound (by passing the ID of the 'snd ' resource
  94. // in your program).  It will load it, move it high in memory, and lock it.  It will
  95. // return FALSE if there was an error.  Errors could be due to low memory or because
  96. // the ID you specified was not found.
  97.  
  98. Boolean    LoadASound(short soundID)
  99. {
  100.     Handle        theSoundHandle;
  101.     Boolean        noProblem;
  102.  
  103.     theSoundHandle = Get1Resource('snd ', soundID);
  104.  
  105.     if (theSoundHandle == nil) {            // If the memory manager didn't assign a handle to
  106.         noProblem = FALSE;                    // theSoundHandle, then there was an error.
  107.     } else {
  108.         noProblem = TRUE;
  109.         MoveHHi(theSoundHandle);
  110.         HLock(theSoundHandle);
  111.     }
  112.  
  113.     return(noProblem);                        // Report back to calling routine.
  114. }
  115.  
  116. //--------------------------------------------------------------  LoadARangeOfSounds
  117.  
  118. // This function loads a range of sounds (say from ID = 2000 to ID = 2014).  You must
  119. // be quite sure however that all those sounds exist.  If anyone of them cannot be
  120. // found (or if memory is too low to permit them all to load) the function will return
  121. // FALSE indicating a failure to load one or more of the specified sounds.  Note that
  122. // this function just makes subsequent calls to the above function.
  123.  
  124. Boolean    LoadARangeOfSounds(short firstID, short lastID)
  125. {
  126.     short    i;
  127.     Boolean    noProblem;
  128.  
  129.     noProblem = TRUE;                        // Start by assuming there won't be an error.
  130.     
  131.     for (i = firstID; i <= lastID; i++) {
  132.         if (! LoadASound(i)) {                // If LoadASound returned FALSE on any sound
  133.             noProblem = FALSE;                //   in the range, there was a problem.
  134.         }
  135.     }
  136.  
  137.     return(noProblem);                        // Report back to calling routine.
  138. }
  139.  
  140. //--------------------------------------------------------------  LoadAllSounds
  141.  
  142. // This function takes a different strategy for loading sounds.  It uses the Resource
  143. // Manager routines to indicate the number of 'snd ' resources in your program
  144. // and then loads every one of them in the order that they appear in the resource fork.
  145.  
  146. Boolean    LoadAllSounds()
  147. {
  148.     Handle        theSoundHandle;
  149.     short        numberOfSounds, i;
  150.     Boolean        noProblem;
  151.  
  152.     noProblem = TRUE;                        // Start by assuming there won't be an error.
  153.  
  154.     numberOfSounds = Count1Resources('snd ');    // Determine # of sounds.
  155.     
  156.     for (i = 1; i <= numberOfSounds; i++) {    // Loop through all sounds.
  157.         theSoundHandle = Get1IndResource('snd ', i);    // Get each sound.
  158.  
  159.         if (theSoundHandle != nil) {        // Did we get a valid sound?
  160.             MoveHHi(theSoundHandle);        // Move sound handle high in memory
  161.             HLock(theSoundHandle);            //   And lock it!
  162.         } else {
  163.             noProblem = FALSE;                // But if we didn't get a valid sound, then we failed.
  164.         }
  165.     }
  166.     
  167.     return(noProblem);                        // Report back to calling routine.
  168. }
  169.  
  170. //--------------------------------------------------------------  ReleaseASound
  171.  
  172. // Call this function to unlock a specific sound (by passing the ID of the 'snd ' resource
  173. // in your program). Unlocking allows the memory manager to move or purge if needed.
  174. // It will return FALSE if there was an error.  Errors could be due to low memory (since it
  175. // will load first if the resource is not in memory) or because the ID you specified was not found.
  176.  
  177. Boolean ReleaseASound(short soundID)
  178. {
  179.     Handle        theSoundHandle;
  180.     Boolean        noProblem;
  181.  
  182.     theSoundHandle = Get1Resource('snd ', soundID);    // Finds sound handle in memory if it's loaded,
  183.                                                     //  otherwise loads it, so HUnlock will have a valid
  184.                                                     //  block to unlock.
  185.     if (theSoundHandle == nil) {
  186.         noProblem = FALSE;
  187.     } else {
  188.         noProblem = TRUE;
  189.         HUnlock(theSoundHandle);                    // Unlocks block, so Memory Manager can move or
  190.     }                                                //   purge it as needed.
  191.  
  192.     return(noProblem);                                // Report back to calling routine.
  193. }
  194.  
  195. //--------------------------------------------------------------  ReleaseARangeOfSounds
  196.  
  197. // This function unlocks a range of sounds (say from ID = 2000 to ID = 2014).  You must
  198. // be quite sure that all those sounds exist, though they need not be loaded into memory.
  199. // If anyone of them cannot be found (or if memory is too low to permit one to load) the function
  200. // will return FALSE indicating a failure to unlock one or more of the specified sounds.
  201. // Note that this function just makes subsequent calls to the above function.
  202.  
  203. Boolean    ReleaseARangeOfSounds(short firstID, short lastID)
  204. {
  205.     short        i;
  206.     Boolean        noProblem;
  207.  
  208.     noProblem = TRUE;                                // Start by assuming there won't be an error.
  209.     
  210.     for (i = firstID; i <= lastID; i++) {
  211.         if (! ReleaseASound(i)) {                    // If ReleaseASound returned FALSE on any sound
  212.             noProblem = FALSE;                        //   in the range, there was a problem.
  213.         }
  214.     }
  215.  
  216.     return(noProblem);                                // Report back to calling routine.
  217. }
  218.  
  219. //--------------------------------------------------------------  ReleaseAllSounds
  220.  
  221. // This function takes a different strategy for unlocking sounds.  It uses a Resource
  222. // Manager routine to indicate the number of 'snd ' resources in your program
  223. // and then unlocks every one of them in the order that they appear in the resource fork.
  224.  
  225. Boolean    ReleaseAllSounds()
  226. {
  227.     Handle        theSoundHandle;
  228.     short        numberOfSounds, i;
  229.     Boolean        noProblem;
  230.  
  231.     noProblem = TRUE;                                // Start by assuming there won't be a problem.
  232.  
  233.     numberOfSounds = Count1Resources('snd ');        // Determine # of sounds.
  234.     
  235.     for (i = 1; i <= numberOfSounds; i++) {            // Loop through all sounds.
  236.         theSoundHandle = Get1IndResource('snd ', i);// Get each sound.
  237.  
  238.         if (theSoundHandle != nil) {                // Did we get a valid sound?
  239.             HUnlock(theSoundHandle);                // And unlock it!
  240.         } else {
  241.             noProblem = FALSE;                        // But if we didn't get a valid sound, then we failed.
  242.         }
  243.     }
  244.     
  245.     return(noProblem);                                // Report back to calling routine.
  246. }
  247.  
  248. //--------------------------------------------------------------  SoundCallBack
  249.  
  250. // This procedure is never called from within the program.  The Mac will call this
  251. // procedure when the Sound Manager gets to a call back command in the sound queue.
  252. // Because this procedure gets called at interrupt time.  You cannot call any routine
  253. // that moves memory or creates it!  No GetResource(), NewHandle(), etc.
  254. // Furthermore, this procedure must always be present in memory!  For this to be
  255. // guaranteed, you should make sure the routine is in the Main segment of your
  256. // program. Put the whole SoundUnit in the Main memory segment in order to be safe.
  257. // This procedure sets soundPriority to zero, so other routines
  258. // can verify that a sound is no longer playing.
  259.  
  260.                 // Toolbox callback routines *must* be declared pascal
  261. pascal void    SoundCallBack(SndChannelPtr chan, SndCommand theCommand)
  262. {
  263.     long    theA5;
  264.     
  265.     if (theCommand.param1 == kOurCallBack) {        // Make sure it's our callBack.
  266.         theA5 = SetA5(theCommand.param2);            // The A5 when InstallCallBack Routine was called.
  267.         soundPriority = kNoSoundPlaying;            // Let the program know the sound is done.
  268.         theA5 = SetA5(theA5);                        // Restore the original A5. See IM6, 22-79.
  269.     }
  270. }
  271.  
  272. //--------------------------------------------------------------  InstallCallBack
  273.  
  274. // This function also is only used internally by the SoundUnit.  It is called right after
  275. // a sound is played asynchronously.  It will install the callBack command into the
  276. // sound queue so that after a sound finishes playing, our call back routine is called
  277. // and we can be alerted to the fact that a sound has finished playing.  This function
  278. // will return any error encountered.
  279.  
  280. OSErr    InstallCallBack()
  281. {
  282.     SndCommand    theCommand;
  283.     
  284.     theCommand.cmd = callBackCmd;                    // Set up the call back command.
  285.     theCommand.param1 = kOurCallBack;                // Flag this call back command as our own.
  286.     theCommand.param2 = SetCurrentA5();
  287.                                                     // Load the command into our sound channel.
  288.     return(SndDoCommand(soundChannel, &theCommand, FALSE));
  289. }
  290.  
  291. //--------------------------------------------------------------  IsSoundOn
  292.  
  293. // This function can be called to determine if sound is both desired and possible.
  294.  
  295. Boolean    IsSoundOn()
  296. {
  297.     return (canUseSound && gUserWantsSound);
  298. }
  299.  
  300. //--------------------------------------------------------------  ASoundIsPlaying
  301.  
  302. // A simple utility function that returns TRUE is a sound is playing or FALSE if not.
  303. // This is how you can externally determine if a sound is playing.
  304.  
  305. Boolean    ASoundIsPlaying()
  306. {
  307.     return(soundPriority != kNoSoundPlaying);
  308. }
  309.  
  310. //--------------------------------------------------------------  SoundPriorityPlaying
  311.  
  312. // A simple utility function that returns an integer indicating the priority of the
  313. // current sound playing.  It will return zero (kNoSoundPlaying) if that is the case.
  314. // This is how you can externally check on the state of the variable 'soundPriority'.
  315.  
  316. short    SoundPriorityPlaying()
  317. {
  318.     return(soundPriority);
  319. }
  320.  
  321. //--------------------------------------------------------------  ThisMacCanPlaySounds
  322.  
  323. // This utility allows the rest of your program access to the 'canUseSound' variable
  324. // that the SoundUnit uses.  This function will return TRUE if it is possible to play
  325. // sound on this Mac or FALSE if it is not.  As an example of this routines use, you
  326. // may have a menu item called 'Sound On' that the user can check or uncheck in
  327. // order to turn sounds in the game on or off.  If however they are running on a Mac
  328. // where it is impossible to play sounds, you would probably rather gray-out the menu
  329. // item entirely.  A call to the below function will let you know if sound is even an option.
  330.  
  331. Boolean    ThisMacCanPlaySounds()
  332. {
  333.     return(canUseSound);
  334. }
  335.  
  336.  
  337. //--------------------------------------------------------------  FlushSoundNow
  338.  
  339. // This routine is called internally to stop any sound currently playing and empty the…
  340. // …command queu of all upcoming sounds and commands.
  341.  
  342. void    FlushSoundNow (void)
  343. {
  344.     SndCommand    thisCommand;
  345.     OSErr        thisErr;
  346.  
  347.     thisCommand.cmd = flushCmd;    // Flush any upcoming commands from the command queu.
  348.     thisCommand.param1 = 0;        // This command ignores these parameters.
  349.     thisCommand.param2 = 0L;
  350.     thisErr = SndDoImmediate(soundChannel, &thisCommand);
  351.     
  352.     thisCommand.cmd = quietCmd;    // Send quietCmd to stop any current sound.
  353.     thisCommand.param1 = 0;        // This command ignores these parameters.
  354.     thisCommand.param2 = 0L;
  355.     thisErr = SndDoImmediate(soundChannel, &thisCommand);
  356. }
  357.  
  358. //--------------------------------------------------------------  FlushSoundSoon
  359.  
  360. // This routine is called internally to empty the command queu of all upcoming sounds…
  361. // …and commands, but leave the current sound playing.
  362.  
  363. void    FlushSoundSoon (void)
  364. {
  365.     SndCommand    thisCommand;
  366.     OSErr        thisErr;
  367.  
  368.     thisCommand.cmd = flushCmd;    // Flush any upcoming commands from the command queu.
  369.     thisCommand.param1 = 0;        // This command ignores these parameters.
  370.     thisCommand.param2 = 0L;
  371.     thisErr = SndDoImmediate(soundChannel, &thisCommand);
  372. }
  373.  
  374. // =========================================================================================\\
  375. //                             The Play…Sound routines                                            \\
  376. // =========================================================================================\\
  377. //    Here is where the results come from. At last, some routines that make sounds come out.    \\
  378. // =========================================================================================\\
  379.  
  380. //--------------------------------------------------------------  PlayASound
  381.  
  382. // Here we are.  This is the work horse routine used by the game to play sounds
  383. // asynchronously.  After starting the sound playing, control is…
  384. // immediately returned to our program.  This allows the action to continue!  You need
  385. // simply specify the ID of the sound you want to play as well as the priority at which
  386. // you want to play the sound (1 - 100).  If a sound of higher priority is already playing,
  387. // then the sound will not be played.
  388.  
  389. OSErr    PlayASound(short soundID, short priority)
  390. {
  391.     OSErr        theErr;
  392.     Handle        theSoundHandle;
  393.  
  394.     if (! IsSoundOn()) return;                        // Exit if we are not to play sounds.
  395.     
  396.     if (priority < soundPriority) return;            // Exit if a higher priority sound is playing.
  397.  
  398.     theSoundHandle = Get1Resource('snd ', soundID);    // Get the sound (only from our resource fork).
  399.     if (theSoundHandle == nil) return;                // Exit if we couldn't get the sound.
  400.  
  401.     if (! soundChannel == nil)                // If soundChannel is open, flush out…
  402.         FlushSoundNow();                    // …any commands in the soundChannel queu,…
  403.                                             // …and quiet the sound currently playing.
  404.     else                                                
  405.     {                                                // Create our sound channel.
  406.         theErr = SndNewChannel(&soundChannel, 0, initMono, (SndCallBackUPP)&SoundCallBack);
  407.                 if (theErr != noErr) return;        // Exit if that failed.
  408.     }
  409.                                             // Play the sound asynchronously.
  410.     theErr = SndPlay(soundChannel, (SndListHandle)theSoundHandle, TRUE);
  411.     if (theErr != noErr) return;            // And exit if that failed.
  412.     
  413.     soundPriority = priority;        // Establish globally the priority of the sound playing.
  414.     
  415.     InstallCallBack();        // Lastly, queue up a callBackCmd to notify us…
  416.                             // when the sound has finished playing.
  417.     return(theErr);
  418. }
  419.  
  420.  
  421. //--------------------------------------------------------------  PlaySynchSound
  422.  
  423. // If you call this routine, the sound will be played and your program will wait here
  424. // until the sound is finished (playing synchronously).  Like the above routine, a sound
  425. // priority must be passed in as well as the ID of the sound to be played synchronously.
  426. // To override any other sound playing, pass it kHighestPriority for the priority.
  427.  
  428. OSErr    PlaySynchSound(short soundID, short priority)
  429. {
  430.     OSErr        theErr;
  431.     Handle        theSoundHandle;
  432.     
  433.     if (! IsSoundOn()) return;                        // Exit if we are not to play sounds.
  434.     
  435.     if (priority < soundPriority) return;            // Exit if a higher priority sound is playing.
  436.  
  437.     theSoundHandle = Get1Resource('snd ', soundID);    // Get the sound (only from our resource fork).
  438.     if (theSoundHandle == nil) return;                // Exit if we couldn't get the sound.
  439.  
  440.     if (! soundChannel == nil)                // If soundChannel is open, flush out…
  441.         FlushSoundNow();                    // …any commands in the soundChannel queu,…
  442.                                             // …and quiet the sound currently playing.
  443.     else                                                
  444.     {                                                // Create our sound channel.
  445.         theErr = SndNewChannel(&soundChannel, 0, initMono, (SndCallBackUPP)SoundCallBack);
  446.                 if (theErr != noErr) return;        // Exit if that failed.
  447.     }
  448.                     // Play the sound synchronously by passing FALSE.
  449.     theErr = SndPlay(soundChannel, (SndListHandle)theSoundHandle, FALSE);
  450.     if (theErr != noErr) return;            // Exit if that failed, but if it works,…
  451.                     // …wait right here while the sound plays.
  452.     soundPriority = 0;        // Establish globally that the sound is done playing.
  453.     return(theErr);
  454. }
  455.  
  456. // =========================================================================================\\
  457. //                         The Feeping Creature routines                                        \\
  458. // =========================================================================================\\
  459. //    "Creeping Featurism" is a threat to good programs. If you throw in everything but…        \\
  460. //    …the kitchen sink, pretty soon you have an abomination like MS Word 6.0.                \\
  461. //    Creeping featurism is particularly disgusting in games,  many of which are so…            \\
  462. //    …complex as to be unplayable.                                                            \\
  463. //    Use these routines with care. Sound should enhance, not overpower, your games.            \\
  464. // =========================================================================================\\
  465.  
  466. //--------------------------------------------------------------  PlayLoopSound
  467.  
  468. // A pretty useful sound routine. It darn near deserves to be above the Feeping Creatures line.
  469. // Same as PlayASound except when PlayLoopSound encounters itself (or a sound with the same…
  470. // …soundPriority number) already playing (or as the most recent entry in the sound channel queu).
  471. // In that case, it calls FlushSoundSoon instead of FlushSound Now, and inserts the new sound…
  472. // …into the queu. The sound currently playing continues to play, followed immediately by the…
  473. // …soundID# passed to PlayLoopSound.
  474. // Useful when making repeated calls for background sounds, to avoid "stuttering" due to the…
  475. // …sound interrupting itself at inappropriate times or due to pausing between loops.
  476. // If you're looping a sound that's six ticks long, and you pass the sound ID# to PlayLoopSound…
  477. // and call it every five ticks, the sound will repeat itself seamlessly forever.
  478.  
  479. OSErr    PlayLoopSound(short soundID, short priority)
  480. {
  481.     OSErr        theErr;
  482.     Handle        theSoundHandle;
  483.  
  484.     if (! IsSoundOn()) return;                        // Exit if we are not to play sounds.
  485.     
  486.     if (priority < soundPriority) return;            // Exit if a higher priority sound is playing.
  487.  
  488.     //SysBeep(10);    //TEST
  489.     
  490.     theSoundHandle = Get1Resource('snd ', soundID);    // Get the sound (only from our resource fork).
  491.     if (theSoundHandle == nil) return;                // Exit if we couldn't get the sound.
  492.  
  493.     if (! soundChannel == nil)                // If soundChannel is open, flush out any upcoming…
  494.         FlushSoundSoon();                    // … commands in the soundChannel queu.
  495.     else                                                
  496.     {                                                // Create our sound channel.
  497.         theErr = SndNewChannel(&soundChannel, 0, initMono, (SndCallBackUPP)&SoundCallBack);
  498.                 if (theErr != noErr) return;        // Exit if that failed.
  499.     }
  500.                                             // Play the sound asynchronously.
  501.     theErr = SndPlay(soundChannel, (SndListHandle)theSoundHandle, TRUE);
  502.     if (theErr != noErr) return;            // And exit if that failed.
  503.     
  504.     soundPriority = priority;        // Establish globally the priority of the sound playing.
  505.     
  506.     InstallCallBack();        // Lastly, queue up a callBackCmd to notify us…
  507.                             // when the sound has finished playing.
  508.     return(theErr);
  509. }
  510.  
  511.  
  512. //--------------------------------------------------------------  PlayLoopSoundOften
  513.  
  514. // This stuffs soundChannel with a loop that repeats over and over and over and over again.
  515. // Use sparingly, or the player will get real real sick of it. Real real real real sick of it.
  516. // Note the sound channel holds a finite number of commands (128 I think), and this routine…
  517. // …will run synchronously (that is, play the sound over and over while everything else is…
  518. // …locked up) for any sounds the queu can't hold. I don't pass more than 100 howManyLoops,…
  519. // …but there's a commented-out check for that if you need it.
  520.  
  521. OSErr    PlayLoopSoundOften(short soundID, short priority, short howManyLoops)
  522. {
  523.     OSErr        theErr;
  524.     Handle        theSoundHandle;
  525.     int            count;
  526.  
  527.     if (! IsSoundOn()) return;                    // Exit if we are not to play sounds.
  528.     
  529.     if (priority < soundPriority) return;        // Exit if a higher priority sound is playing.
  530.  
  531. //    if (howManyLoops > 100)                        // If an excessively large number is passed,…
  532. //            howManyLoops = 100;                    // …reduce it so it won't overflow the queu.
  533.  
  534.     theSoundHandle = Get1Resource('snd ', soundID);    // Get the sound (only from our resource fork).
  535.     if (theSoundHandle == nil) return;                // Exit if we couldn't get the sound.
  536.  
  537.     if (! soundChannel == nil)                // If soundChannel is open, flush out everything…
  538.         FlushSoundNow();                    // …from the soundChannel command queu.
  539.     else                                                
  540.     {                                                // Create our sound channel.
  541.         theErr = SndNewChannel(&soundChannel, 0, initMono, (SndCallBackUPP)&SoundCallBack);
  542.                 if (theErr != noErr) return;        // Exit if that failed.
  543.     }
  544.     for (count = 1; count <= howManyLoops; count++)
  545.     {                                        // Stuff the sound queu with sounds to play,…
  546.         theErr = SndPlay(soundChannel, (SndListHandle)theSoundHandle, TRUE);
  547.         if (theErr != noErr) return;            // …and exit if that failed.
  548.     }
  549.     soundPriority = priority;        // Establish globally the priority of the sound playing.
  550.     
  551.     InstallCallBack();                // Lastly, queue up a callBackCmd to notify us…
  552.                                     // …when the sound has finished playing.
  553.     return(theErr);
  554. }
  555.  
  556. //--------------------------------------------------------------  AddSoundToQ
  557.  
  558. // AddSoundToQ does not flush the command queue from soundChannel, nor silence soundChannel, nor…
  559. // …check the priority of the sound currently playing, nor establish its own priority.
  560. // What it does is add (a command to play the selected sound) to the end of the soundChannel…
  561. // …command queu, and set soundPriority to zero.
  562. // Any call to any other Play…Sound routine will interrupt AddSoundToQ, and…
  563. // …AddSoundToQ will add its sound to the queu created by any Play…Sound routine. This routine…
  564. // …resides outside the entire soundPriority system, and must be called with care.
  565. // So what's AddSoundToQ good for? Stringing a series of sounds together. Put individual words…
  566. // in resource files, and play them back as sentences. Record assorted barnyard animal sounds,…
  567. // …sort them by pitch, store them as 'snd ' resources, and play them back as music.
  568. // The standard sound queu holds 128 commands; that's enough for some quite complex dialog…
  569. //…from ground control, or for a flock of sheep to baa a full chorus of "In the Mood," but don't…
  570. // …go over 128, or it'll play synchronously (that is, everything but the sound will lock up)…
  571. // …until the queue works its way back down to 128 commands.
  572.  
  573. void AddSoundToQ (short soundID)
  574. {
  575.     OSErr        theErr;
  576.     Handle        theSoundHandle;
  577.     
  578.         if (! IsSoundOn()) return;                    // Exit if we are not to play sounds.
  579.     
  580.         theSoundHandle = Get1Resource('snd ', soundID);    // Get the sound (only from our resource fork).
  581.         if (theSoundHandle == nil) return;                // Exit if we couldn't get the sound.
  582.  
  583.         if ( soundChannel == nil)                // If there's no soundChannel, open it.
  584.             theErr = SndNewChannel(&soundChannel, 0, initMono, (SndCallBackUPP)&SoundCallBack);
  585.                 if (theErr != noErr) return;        // Exit if that failed.
  586.  
  587.                                             // Add this sound to the the command queue.
  588.         theErr = SndPlay(soundChannel, (SndListHandle)theSoundHandle, TRUE);
  589.             
  590.         soundPriority = 0;            // Set it here instead of using the sound callback…
  591.                                     // …routine--saves queue space.
  592. }
  593.  
  594. //------------------------------------------------------------------------------------------\\
  595. //                                    End MGWSound1.c                                            \\
  596. //------------------------------------------------------------------------------------------\\
  597.